export type Method = WechatMiniprogram.RequestOption['method'] | 'UPLOAD'
export type Options =
  | WechatMiniprogram.RequestOption
  | (WechatMiniprogram.UploadFileOption & { method: Method })
export type RequestData = WechatMiniprogram.RequestOption['data']
export type RequestConfig = Omit<
  WechatMiniprogram.RequestOption,
  'url' | 'method' | 'data' | 'success' | 'fail' | 'complete'
>
export type UploadData = Pick<
  WechatMiniprogram.UploadFileOption,
  'name' | 'filePath' | 'formData'
>
export type UploadConfig = Omit<
  WechatMiniprogram.UploadFileOption,
  'url' | 'name' | 'filePath' | 'formData' | 'success' | 'fail' | 'complete'
>
export interface ResponseResultData<T = any> {
  [key: string]: any
  data: T
}
export type FunctionType<T = any> = (
  url: string,
  data?: RequestData,
  config?: RequestConfig
) => Promise<ResponseResultData<T>>;
export type UploadFunctionType<T = any> = (
  url: string,
  data: UploadData,
  config?: UploadConfig
) => Promise<ResponseResultData<T>>;

export interface Request {
  <T = ResponseResultData>(options: Options): Promise<T>
  /**
   * 配置接口基础路径
   */
  baseURL?: string
  /**
   * 请求状态配置，同wx.showLoading
   */
  loading: WechatMiniprogram.ShowLoadingOption
  /**
   * 请求拦截器
   */
  intercept: {
    request(options: Options): Options
    response(
      result:
        | (WechatMiniprogram.RequestSuccessCallbackResult & {
          config: Options
        })
        | (WechatMiniprogram.UploadFileSuccessCallbackResult & {
          config: Options
        })
    ): any
  }

  /**
   * GET 方法请求
   */
  get: FunctionType

  /**
   * POST 方法请求
   */
  post: FunctionType

  /**
   * PUT 方法请求
   */
  put: FunctionType

  /**
   * DELETE 方法请求
   */
  delete: FunctionType

  /**
   * wx.uploadFile 上传文件
   */
  upload: UploadFunctionType
}

function create(config = { showLoading: true }) {
  // 记录 loading 的状态
  const loadingQueue: string[] = []
  const request: Request = (options) => {
    // 处理基础路径
    if (!options.url.startsWith('http') && request.baseURL) {
      // 去除 baseURL 最后的 /
      if (request.baseURL.endsWith('/')) {
        request.baseURL = request.baseURL.slice(0, -1)
      }
      // 拼接 baseURL
      options.url = request.baseURL + options.url
    }

    // 调用拦截器处理请求数据
    options = request.intercept.request(options)

    // 记录请求开始的次量
    loadingQueue.push('loading')

    // 是否显示加载 loading
    if (config.showLoading && loadingQueue.length) {
      wx.showLoading(request.loading)
    }

    // 非上传文件请求
    if (options.method === 'UPLOAD') {
      // 包装 Promise 对象
      return new Promise((resolve, reject) => {
        // 联合类型断言
        const _options = options as WechatMiniprogram.UploadFileOption
        // 调用 wx.uploadFile 上传文件
        wx.uploadFile({
          ..._options,
          success: (result) => {
            // 将响应数据转为对象
            result.data = JSON.parse(result.data)
            // 调用拦截器处理响应数据
            resolve(request.intercept.response({ ...result, config: options }))
          },
          fail: reject,
          complete: () => {
            // 记录结束的请求数量
            loadingQueue.pop()
            // 关闭加载提示框
            if (!loadingQueue.length) wx.hideLoading()
          },
        })
      })
    }
    // 包装 Promise 对象
    return new Promise((resolve, reject) => {
      // 联合类型断言
      const _options = options as WechatMiniprogram.RequestOption
      // 调用 wx.request 发送请求
      wx.request({
        ..._options,
        success: (result) => {
          // 调用拦截器处理响应数据
          resolve(request.intercept.response({ ...result, config: options }))
        },
        fail: reject,
        complete: () => {
          // 记录结束的请求数量
          loadingQueue.pop()
          // 关闭加载提示框
          if (!loadingQueue.length) wx.hideLoading()
        },
      })
    })
  }

  // get 方法请求
  request.get = (url, data, config) => request({ method: 'GET', url, data, ...config });
  // post 方法请求
  request.post = (url, data, config) => request({ method: 'POST', url, data, ...config });
  // put 方法请求
  request.put = (url, data, config) => request({ method: 'PUT', url, data, ...config });
  // delete 方法请求
  request.delete = (url, data, config) => request({ method: 'DELETE', url, data, ...config });
  // upload 方法请求
  request.upload = (url, data, config) => request({ method: 'UPLOAD', url, ...data, ...config });

  /**
   * 默认loading配置
   */
  request.loading = {
    title: '正在加载',
    mask: false,
  }

  /**
   * 默认拦截器（什么也没做）
   */
  request.intercept = {
    request: (options) => options,
    response: (result) => result,
  }
  return request
}

export const request = create();

// get 方法请求
export const get: FunctionType = (url, data, config) => request.get(url, data, config);
// post 方法请求
export const post: FunctionType = (url, data, config) => request.post(url, data, config);
// put 方法请求
export const put: FunctionType = (url, data, config) => request.put(url, data, config);
// delete 方法请求
export const del: FunctionType = (url, data, config) => request.delete(url, data, config);
// upload 方法请求
export const upload: UploadFunctionType = (url, data, config) => request.upload(url, data, config);

export default request;